package com.example.ribbonconsumer.service.impl;

import java.io.IOException;
import java.util.concurrent.Future;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import com.example.ribbonconsumer.model.Book;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.annotation.ObservableExecutionMode;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheKey;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheRemove;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult;
import com.netflix.hystrix.contrib.javanica.command.AsyncResult;

import rx.Observable;
import rx.Subscriber;

@Service
public class BookService {
	
	@Autowired
	private RestTemplate restTemplate;
	
	/**
	 * 同步方式
	 */
	@CacheResult(cacheKeyMethod="getBookCacheKey")
	@HystrixCommand(fallbackMethod = "defaultBook",ignoreExceptions=IOException.class,
			commandKey="getBookByName",groupKey="bookGroup",threadPoolKey="bookThreadPool",commandProperties={
					//设置HystrixCommand.run()执行的隔离策略，Thread:通过线程池隔离的策略。Semaphone 通过信号量隔离的策略。
					@HystrixProperty(name="execution.isolation.strategy",value="THREAD"),
					//设置HystrixCommand执行的超时时间，单位毫秒
					@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="2000"),
					//设置HystrixCommand.run()的执行是否启用超时时间，默认是true,如果设置为false,则timeoutInMilliseconds不起作用
					@HystrixProperty(name="execution.timeout.enabled",value="false"),
					//设置HystrixCommand.run()的执行超时的时候是否要将它中断,默认是true
					@HystrixProperty(name="execution.isolation.thread.interruptOnTimeout",value="true"),
					//设置HystrixCommand.run()的执行被取消时，是否将它中断，默认是true
//					@HystrixProperty(name="execution.isolation.thread.interruptOnCancel",value="true"),
					//设置HystrixCommand的隔离策略是信号量时，该属性用来配置信号量的大小，当最大并发请求数达到该设置值时，后续的请求会被拒绝。
					//默认是10
					@HystrixProperty(name="execution.isolation.semaphore.maxConcurrentRequests",value="20"),
					
					//设置从调用线程中允许getFullBack()方法执行的最大并发请求数。默认是10
					@HystrixProperty(name="fallback.isolation.semaphore.maxConcurrentRequests",value="20"),
					//设置服务降级 策略是否启用。
					@HystrixProperty(name="fallback.enabled",value="true"),
					
					//用来确定当服务请求命令失败时，是否使用断路器来跟踪其健康指标和熔断请求
					@HystrixProperty(name="circuitBreaker.enabled",value="true"),
					//在滚动时间窗口中，断路器的最小请求数，默认20
					@HystrixProperty(name="circuitBreaker.requestVolumeThreshold",value="30"),
					//用来设置当断路器打开之后的休眠时间窗。默认5000
					@HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds",value="3000"),
					//设置断路器打开的错误百分比条件。默认50
					@HystrixProperty(name="circuitBreaker.errorThresholdPercentage",value="40"),
					//如果设置为true,断路器强制进入打开状态，它会拒绝所有的请求。优先级高于forceClosed
					@HystrixProperty(name="circuitBreaker.forceOpen",value="false"),
					//如果设置为true,断路器强制进入关闭状态，它会接收所有的请求。
					@HystrixProperty(name="circuitBreaker.forceClosed",value="false"),
					
					//设置滚动时间窗口的长度，单位毫秒，默认10000
					@HystrixProperty(name="metrics.rollingStats.timeInMilliseconds",value="20000"),
					//设置滚动时间窗统计指标信息时划分桶的数量，默认10
					@HystrixProperty(name="metrics.rollingStats.numBuckets",value="20"),
					//设置对命令延迟是否使用百分位来跟踪和计算。
					@HystrixProperty(name="metrics.rollingPercentile.enabled",value="false"),
					//设置百分位统计的滚动窗口的持续时间，单位毫秒。
					@HystrixProperty(name="metrics.rollingPercentile.timeInMilliseconds",value="50000"),
					//设置百分位统计的滚动窗口中使用的桶的数量。
					@HystrixProperty(name="metrics.rollingPercentile.numBuckets",value="5"),
					//设置在执行过程中每个桶中保留的最大执行次数
					@HystrixProperty(name="metrics.rollingPercentile.bucketSize",value="120"),
					//设置影响断路器状态的健康快照的间隔等待时间。
					@HystrixProperty(name="metrics.healthSnapshot.intervalInMilliseconds",value="600"),
					
					//是否开启请求缓存
					@HystrixProperty(name="requestCache.enabled",value="true"),
					//是否打印日志
					@HystrixProperty(name="requestLog.enabled",value="true")
			},threadPoolProperties = {
					//命令线程池的核心线程数
					@HystrixProperty(name="coreSize",value="20"),
					//设置线程池的最大队列大小
					@HystrixProperty(name="maxQueueSize",value="20"),
					//为队列设置拒绝阈值。当maxQueueSize为-1,该属性不生效
					@HystrixProperty(name="queueSizeRejectionThreshold",value="10"),
					//设置滚动时间窗的长度，单位毫秒
					@HystrixProperty(name="metrics.rollingStats.timeInMilliseconds",value="10"),
					//设置滚动时间窗被划分成桶的数量
					@HystrixProperty(name="metrics.rollingStats.numBuckets",value="10")
					
			})
	
	
	public Book getBookByName(/*@CacheKey("name")*/ String name){
		return restTemplate.getForEntity("http://HELLO-SERVICE/getbookbyname?name={1}", Book.class, name).getBody();
	}
	
	public Book defaultBook(String name,Throwable e){
		return new Book();
	}
	
	public String getBookCacheKey(String name){
		return name;
	}
	
	/**
	 * 异步方式
	 * @return
	 */
	@HystrixCommand
	public Future<Book> getBookByNameAsync(){
		return new AsyncResult<Book>() {

			@Override
			public Book invoke() {
				return restTemplate.getForEntity("http://HELLO-SERVICE/getbookbyname?name={1}", Book.class, "didi").getBody();
			}
			
		};
	}
	
	/**
	 * ObservableExecutionMode.EAGER 表示使用observe()
	 * ObservableExecutionMode.LAZY 表示使用toObservable()
	 * @return
	 */
	@HystrixCommand(observableExecutionMode=ObservableExecutionMode.LAZY)
	public Observable<Book> getBookByNameObs(){
		return Observable.create(new Observable.OnSubscribe<Book>() {

			@Override
			public void call(Subscriber<? super Book> observer) {
				try {
					if(!observer.isUnsubscribed()){
						Book book = restTemplate.getForEntity("http://HELLO-SERVICE/getbookbyname?name={1}", Book.class, "didi").getBody();
						observer.onNext(book);
						observer.onCompleted();
					}
				} catch (Exception e) {
					observer.onError(e);
				}
			}
		});
	}

	@CacheRemove(commandKey = "getBookByName")
	@HystrixCommand
	public void updateBook(@CacheKey("name")Book book){
		
	}
	
}
